package com.gluck.cases.boostrap.config.shiro;

import com.gluck.cases.common.utils.enums.BizSerErrorEnum;
import com.gluck.cases.common.utils.exception.BizSerException;
import com.gluck.cases.app.biz.impl.RoleService;
import com.gluck.cases.app.biz.impl.AdminUserService;
import com.gluck.cases.app.biz.impl.PermissionService;
import com.gluck.cases.common.utils.utils.JedisUtil;
import com.gluck.cases.common.utils.utils.JwtUtil;
import com.gluck.cases.boostrap.config.shiro.jwt.JwtToken;
import com.gluck.cases.core.modal.constants.AuthConstants;
import com.gluck.cases.core.modal.entity.Permission;
import com.gluck.cases.core.modal.entity.Role;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Objects;
import java.util.Set;

/**
 * @ClassName AdminRealm
 * @Desc 自定义Realm
 * @Author I am Gluck
 * @Date 2022/5/17 - 5:42 下午
 **/
@Service
public class AdminRealm extends AuthorizingRealm {

    @Autowired
    private AdminUserService adminUserService;
    @Autowired
    private RoleService roleService;
    @Autowired
    private PermissionService permissionService;

    /**
     * 大坑，必须重写此方法，不然Shiro会报错
     */
    @Override
    public boolean supports(AuthenticationToken authenticationToken) {
        return authenticationToken instanceof JwtToken;
    }

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        // step 1 通过token 获取username
        String username = JwtUtil.getClaim(principals.toString(), AuthConstants.USERNAME);
        // step 2 查询用户adminRole
        List<Role> roles = findAdminRoleByUsername(username);
        // step 3 循环添加角色
        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
        roles.stream().forEach(x -> {
            simpleAuthorizationInfo.addRole(x.getName());
            // step 4 根据角色查询权限
            List<Permission> permissions = permissionService.findPermissionByRoleName(x.getName());
            // step 5 设置权限
            Set<String> simpleAuthorizationInfos = addPermission(permissions);
            simpleAuthorizationInfo.setStringPermissions(simpleAuthorizationInfos);

        });
        return simpleAuthorizationInfo;
    }


    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        // step 1 获取TOKEN
        String token = (String) authenticationToken.getCredentials();
        // step 2 解密token 获取username
        String username = JwtUtil.getClaim(token, AuthConstants.USERNAME);
        if (StringUtils.isBlank(username)) {
            throw new BizSerException(BizSerErrorEnum.TOKEN_NOT_FOUND);
        }
        // step 3 查询用户是否存在
        if (!getAdminUserByUsername(username)) {
            throw new BizSerException(BizSerErrorEnum.ADMIN_USER_NOT_FOUND);
        }
        // step 4 校验用户token 不正确就抛出异常
        boolean verify = JwtUtil.verify(token);
        // step 5 开始认证策略
        if (!verify || !JedisUtil.exists(AuthConstants.PREFIX_SHIRO_REFRESH_TOKEN + username)) {
            throw new BizSerException(BizSerErrorEnum.TOKEN_OVER_TIME);
        }
        //step 6 获取RefreshToken的时间戳
        String currentTimeMillisRedis = JedisUtil.getObject(AuthConstants.PREFIX_SHIRO_REFRESH_TOKEN + username).toString();
        if (currentTimeMillisRedis.equals(JwtUtil.getClaim(token, AuthConstants.CURRENT_TIME_MILLIS))) {
            return new SimpleAuthenticationInfo(token, token, "authRealm");
        }
        throw new BizSerException(BizSerErrorEnum.AUTHORIZING_REALM_ERROR);
    }


    private List<Role> findAdminRoleByUsername(String username) {
        List<Role> roles = roleService.findAdminRoleByUsername(username);
        return roles;
    }

    private Set<String> addPermission(List<Permission> permissions) {
        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
        permissions.stream().filter(p -> {
            if(Objects.isNull(p)){
                return false;
            }
            return true;
        }).forEach(x->{
            simpleAuthorizationInfo.addStringPermission(x.getPerCode());
        });
        Set<String> stringPermissions = simpleAuthorizationInfo.getStringPermissions();
        return stringPermissions;
    }

    private boolean getAdminUserByUsername(String username) {
        return adminUserService.existAdminUserByUsername(username);
    }
}
